<?php
namespace Zeke\App;

use Zeke\Contracts\Io\Socket\ConnectionContract;
use Zeke\Io\Loop\Libevent;
use Zeke\Io\Loop\Select;
use Zeke\Net\Http\Server;
use Zeke\Zee\Event\BufferEvent;
use Zeke\Zee\Event\IoEvent;


/**
 * Class Main
 * @package Zeke\App
 *
 * 在父类为子类定义事件处理函数
 * 在子类触发该事件
 *
 * server流转过程：
 *  1: server绑定IoEvent::connect事件，定义connectFunc回调处理函数
 *  2: server对象中的TcpServer绑定IoEvent所有事件，用来触发server的所有IoEvent事件
 *  3: 在TcpServer对象中，loop使用TcpServer绑定addReadStream，定义回调处理函数，结束;
 *
 *  4: loop循环事件是否发生
 *  5: 由 select 或 epoll 触发TcpServer的readStream的回调（生成connect对象，触发server的ioEvent::connect事件）
 *  6: 执行connectFunc回调处理函数，参数为connect对象
 *  本质上server 就是特殊只读的stream，它的addReadStream回调比较固定，accept连接，触发连接事件
 *
 * connect流转过程：
 *  1: 在server的connectFunc回调处理函数中得到connect对象
 *  2: 在connectFunc回调中，connect对象绑定BufferEvent::Data事件，定义DataFunc回调处理函数
 *  3: 在connect对象中，为DuplexResourceStream对象绑定BufferEvent所有事件，用来触发connect对象的BufferEvent的事件（DataFunc回调处理函数）
 *  4: 在DuplexResourceStream对象中，定义WritableResourceStream对象（WritableResourceStream对象内部不使用loop绑定addWriteStream事件）
 *  5: 在DuplexResourceStream对象中，loop使用TcpServer绑定addReadStream，定义回调处理函数，结束;
 *
 *  6: loop循环事件是否发生
 *  7: 由 select 或 epoll 触发DuplexResourceStream对象的readStream的回调（读取socket数据，触发BufferEvent::data事件，即DataFunc回调处理函数）
 *  8: 执行DataFunc回调处理函数，参数为socket读取的数据
 *  9: 在DataFunc回调处理函数中，调用connect对象的write方法
 *  10: 在write方法中，loop使用DuplexResourceStream.WritableResourceStream对象绑定addWriteStream事件，定义回调处理函数，结束;
 *
 *  11: 由 select 或 epoll 触发WritableResourceStream对象的回调（执行fwrite系统写入函数，短连接http则关闭socket）
 *  本质上，connect就是一个读写stream
 */


class Main
{


    public function main4()
    {
        $loop = new Libevent();

        $server = stream_socket_server('tcp://127.0.0.1:8080');
        stream_set_blocking($server, false);

        $loop->addReadStream($server, function ($server) use ($loop) {

            $conn = stream_socket_accept($server);


            $content    = "hi-".time();
            $length     = strlen($content);
            $data       = "HTTP/1.1 200 OK\r\nContent-Length: ".$length."\r\n\r\n".$content."\n";

            $loop->addWriteStream($conn, function ($conn) use (&$data, $loop) {
                $written = fwrite($conn, $data);
                if ($written === strlen($data)) {
                    fclose($conn);
                    $loop->removeWriteStream($conn);
                } else {
                    $data = substr($data, $written);
                }
            });
        });


        $loop->run();


    }



    public function main7()
    {

        $loop = new Select();



        $server = stream_socket_server('tcp://127.0.0.1:8080');
        stream_set_blocking($server, false);

        $loop->addReadStream($server, function ($server) use ($loop) {



            $loop->futureTick(function () use ($server){
                echo "accept - (".(int)$server.")\n";
            });


            $conn = stream_socket_accept($server);
            $data = "HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nHi\n";
            $loop->addWriteStream($conn, function ($conn) use (&$data, $loop) {
                $written = fwrite($conn, $data);
                if ($written === strlen($data)) {
                    fclose($conn);
                    $loop->removeWriteStream($conn);
                } else {
                    $data = substr($data, $written);
                }
            });
        });



        //

        $loop->futureTick(function () {
            echo "2\n";
        });


        echo "1\n";
        $loop->run();

    }


    public function main6()
    {

        $name = "liLi\n";
        $loop = new Select();
        $loop->futureTick(function () use ($name) {
            echo "hello $name\n";
        });


        $loop->futureTick(function () use ($name) {
            echo "hello $name\n";
        });

        $loop->futureTick(function () use ($name) {
            echo "hello $name\n";
        });



        $timer = $loop->addPeriodicTimer(1, function () {
            echo 'tick!' . PHP_EOL;
        });

        $loop->run();
    }


    public function main5()
    {

        $loop = new Select();

        $timer = $loop->addPeriodicTimer(0.1, function () {
            echo 'tick!' . PHP_EOL;
        });

        $loop->addTimer(1.0, function () use ($loop, $timer) {
            $loop->cancelTimer($timer);
            echo 'Done' . PHP_EOL;
        });



//        $hello = 'hello';
//        $world = 'world';
//        $loop->addTimer(0.3, function () use ($hello){
//            echo 'hello ';
//        });
//        $loop->addTimer(0.8, function () use ($world){
//            echo 'world!' . PHP_EOL;
//        });

        $loop->run();

    }


    /**
     * http server
     *
     * @author liu.bin 2018/8/14 9:28
     */
    public function main3()
    {
//        $loop = new Select();
//
//        $server = new Server(function (Psr\Http\Message\ServerRequestInterface $request) {
//            return new React\Http\Response(
//                200,
//                array('Content-Type' => 'text/plain'),
//                "Hello World!\n"
//            );
//        });
//
//        $socket = new \Zeke\Io\Socket\Server(8080, $loop);
//        $server->listen($socket);
//
//        echo "Server running at http://127.0.0.1:8080\n";
//
//        $loop->run();
    }


    /**
     * socket
     *
     * @author liu.bin 2018/8/14 9:28
     */
    public function main()
    {
        $loop = new Select();

        $opts = array('backlog'=>1024);
        $socket = new \Zeke\Io\Socket\Server('127.0.0.1:8080', $loop,$opts);
        $socket->on(IoEvent::CONNECT, function (ConnectionContract $conn) {


            $key = (int) $conn->stream;
            echo "client[{$key}] connect\n";


            $conn->on(BufferEvent::DATA, function ($data) use ($conn) {

                //echo $data;
                $content= "hihi-".time();
                $length = strlen($content);
                $res    = "HTTP/1.1 200 OK\r\nContent-Length: ".$length."\r\n\r\n".$content."\r\n";
                /**
                 * 如果只做一次请求响应即断开
                 * 不可以在fwrite()下面直接调用close()方法，需要在addWriteStream的回调调用close()
                 * end()则实现的短连接
                 */
                $conn->end($res);

            });


        });


        $loop->addPeriodicTimer(5, function () {
            $memory = memory_get_usage() / 1024;
            $k_memory = number_format($memory, 3).'K';
            $m_memory = number_format($memory / 1024,3).'M';


            $totalMemory = memory_get_usage(true) / 1024;
            $k_tm = number_format($totalMemory,3).'K';
            $m_tm = number_format($totalMemory / 1024,3).'M';

            $peakMemory = memory_get_peak_usage() / 1024;
            $k_pm = number_format($peakMemory,3).'K';
            $m_pm = number_format($peakMemory / 1024,3).'M';


            $peakTotalMemory = memory_get_peak_usage(true) / 1024;
            $k_ptm = number_format($peakTotalMemory,3).'K';
            $m_ptm = number_format($peakTotalMemory / 1024,3).'M';

            echo "PHP Script Current memory usage: { {$k_memory} - {$m_memory} }   - ".date("Y-m-d H:i:s",time()) ."\n";
            echo "PHP Script Total memory usage:   { {$k_tm} - {$m_tm} }   - ".date("Y-m-d H:i:s",time()) ."\n";
            echo "PHP Process Current memory usage:{ {$k_pm} - {$m_pm} }   - ".date("Y-m-d H:i:s",time()) ."\n";
            echo "PHP Process Total memory usage:   { {$k_ptm} - {$m_ptm} }   - ".date("Y-m-d H:i:s",time()) ."\n\n";
        });

        $loop->run();
    }


    /**
     * loop
     *
     * @author liu.bin 2018/8/1 17:30
     */
    public function main2()
    {

        $loop = new Select();

        $server = stream_socket_server('tcp://127.0.0.1:8080');
        stream_set_blocking($server, false);

        $loop->addReadStream($server, function ($server) use ($loop) {

            $conn = stream_socket_accept($server);
            $data = "HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nHi\n";
            $loop->addWriteStream($conn, function ($conn) use (&$data, $loop) {
                $written = fwrite($conn, $data);
                if ($written === strlen($data)) {
                    fclose($conn);
                    $loop->removeWriteStream($conn);
                } else {
                    $data = substr($data, $written);
                }
            });
        });

        $loop->run();


    }

}